/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package UI;

import Algorithm.Pathfinding;
import GameData.Map;
import GameLogic.Game.GameState;
import GameLogic.IGame.Updatable;
import GameLogic.*;
import java.awt.*;
import java.awt.event.MouseEvent;
import java.awt.geom.Point2D;
import java.awt.image.BufferStrategy;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.LinkedList;
import javax.swing.*;

/**
 *
 * @author ix
 */
public class MainForm extends javax.swing.JFrame 
{
    TowerMenu TMenu;
    public class TowerMenu
    {
        JLabel pan;
        TowerType[] Towers;
        public int selected = -1;
        GameTooltip tip = new GameTooltip(MainForm.this);
        
        int offset = 6,
            height = 70;
        
        public void Redraw()
        {
            int w = pan.getWidth(),
                h = pan.getHeight();
            BufferedImage img = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
            Graphics2D g = img.createGraphics();
            g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
            for(int i = 0; Towers[i] != null; i++)
            {
                int baseX = offset,
                    baseY = offset + (height + offset) * i;
                
                //draw background
                if(selected == i)
                    g.setColor(new Color(230, 230, 20));
                else
                    g.setColor(new Color(20, 20, 20));
                g.fillRect(baseX, baseY, w-offset*2, height);
                baseX += 2;
                baseY += 2;
                if(MainForm.this.CurrentGame.canBuildTowerType(Towers[i]))
                    g.setColor(new Color(90, 90, 90));
                else
                    g.setColor(new Color(40, 40, 40));
                g.fillRect(baseX, baseY, w-offset*2 - 4, height - 4);
                
                //draw tower
                int towX = baseX + 6;
                int towY = baseY + 6;
                DrawTower(Towers[i].color, Towers[i].size, g, new Point(towX, towY), (height - 24)/Towers[i].size, (height - 24)/Towers[i].size, null);
                
                //draw size label
                int sizeY = baseY + height - 24 + 3 + 14;
                int sizeX = baseX + (height - 24) / 2 - 1;
                String s = String.valueOf(Towers[i].size);
                Font f = new Font("dialog", Font.BOLD, 9);
                g.setFont(f);
                g.setColor(new Color(240, 170, 20));
                g.drawString(s + "x" + s, sizeX, sizeY);
                
                //draw name
                String n = Towers[i].name;
                f = new Font("serif", Font.BOLD, 14);
                g.setFont(f);
                g.setColor(new Color(240, 240, 200));
                //int l = GetStringPixelLen(n, f);
                int nameX = baseX + height - 24 + 15;
                int nameY = baseY + 20;
                g.drawString(n, nameX, nameY);
                
                //draw gold cost
                f = new Font("monospaced", Font.BOLD, 12);
                g.setFont(f);
                n = String.valueOf(Towers[i].price) + "G";
                int l = GetStringPixelLen(n, f);
                g.setColor(new Color(240, 200, 50));
                int goldX = baseX + (w-offset*2 - 4) - 45;
                int goldY = nameY;
                g.drawString(n, goldX, goldY);
                
                f = new Font("dialog", Font.BOLD, 11);
                g.setFont(f);
                
                int dmgX = nameX + 3;
                int dmgY = goldY + 7 + 15;
                String aName = "";
                if(Towers[i].tAura != null)
                    aName = Towers[i].tAura.name;
                if(Towers[i].mAura != null)
                    aName = Towers[i].mAura.name;
                if(Towers[i].MaxDamage > 0)
                {
                    s = "Damage: ";
                    g.setColor(new Color(240, 200, 50));
                    g.drawString(s, dmgX, dmgY);
                    String v = Towers[i].MinDamage + "-" + Towers[i].MaxDamage;
                    if(Towers[i].dmgType == DamageType.Physical)
                        g.setColor(new Color(220, 210, 170));
                    else if(Towers[i].dmgType == DamageType.Magical)
                        g.setColor(new Color(180, 180, 255));
                    else if(Towers[i].dmgType == DamageType.Chaos)
                        g.setColor(new Color(177, 255, 100));
                    g.drawString(v, dmgX + GetStringPixelLen(s, f), dmgY);
                    
                    int rangeX = dmgX,
                        rangeY = dmgY + 15;
                    s = "Range: ";
                    g.setColor(new Color(240, 200, 50));
                    g.drawString(s, rangeX, rangeY);
                    v = String.valueOf(Towers[i].Range);
                    g.setColor(Color.WHITE);
                    g.drawString(v, rangeX + GetStringPixelLen(s, f), rangeY);
                    
                    v = String.valueOf((double)((Towers[i].Cooldown/10))/100);
                    int cdX = goldX + l - GetStringPixelLen(v, f);
                    int cdY = dmgY;
                    g.setColor(Color.WHITE);
                    g.drawString(v, cdX, cdY);
                    s = "Cd: ";
                    g.setColor(new Color(240, 200, 50));
                    g.drawString(s, cdX - 24, cdY);
                    
                    int auraX = goldX + l - GetStringPixelLen(aName, f);
                    int auraY = rangeY;
                    g.setColor(new Color(230, 230, 255));
                    g.drawString(aName, auraX, auraY);
                }
                else if(aName != "")
                {
                    int auraX = goldX + l - GetStringPixelLen(aName, f);
                    int auraY = dmgY;
                    g.setColor(new Color(230, 230, 255));
                    g.drawString(aName, auraX, auraY);
                }
            }
            pan.setHorizontalAlignment(SwingConstants.LEFT);
            pan.setVerticalAlignment(SwingConstants.TOP);
            pan.setIcon(new ImageIcon(img));
            
            pan.repaint();
        }
        
        public TowerMenu(TowerType[] t, JLabel p)
        {
            int pw = p.getWidth(),
                ph = p.getHeight();
            MainForm.this.remove(p);
            MainForm.this.add(p);
            pan = p;
            p.addMouseListener(new java.awt.event.MouseAdapter() {
                @Override
                public void mouseClicked(java.awt.event.MouseEvent e)
                {
                    int y = e.getY() - offset;
                    int x = e.getX();
                    if(y < 0 || y % (height + offset) > height || x < offset || x + offset > pan.getWidth())   
                    {
                        selected = -1;
                        MainForm.this.SelectedObject = MainForm.NOTHING;
                        btnSell.setVisible(false);
                        btnUpgr.setVisible(false);
                    }
                    else
                    {
                        int i = y / (height + offset);
                        if(i >= Towers.length || Towers[i] == null || 
                           !MainForm.this.CurrentGame.canBuildTowerType(Towers[i]))
                        {
                            selected = -1;
                            MainForm.this.SelectedObject = MainForm.NOTHING;
                            btnSell.setVisible(false);
                            btnUpgr.setVisible(false);
                        }
                        else
                        {
                            selected = i;
                            MainForm.this.SelectedObject = MainForm.TOWERTYPE;
                            btnSell.setVisible(false);
                            btnUpgr.setVisible(false);
                        }
                    }
                    Redraw();
                }
            });
            p.addMouseMotionListener(new java.awt.event.MouseMotionAdapter() {
                public void mouseMoved(java.awt.event.MouseEvent e) 
                {
                    int y = e.getY() - offset;
                    int x = e.getX();
                    if(y < 0 || y % (height + offset) > height || x < offset || x + offset > pan.getWidth())
                    {
                        tip.hide();
                        return;
                    }
                    int i = y / (height + offset);
                    if(i >= Towers.length || Towers[i] == null)
                    {
                        tip.hide();
                        return;
                    }
                    y += pan.getY() + 23;
                    x += pan.getX() + 3;
                    if(MainForm.this.CurrentGame.canBuildTowerType(Towers[i]))
                        tip.show(x, y, Towers[i].desc);
                    else
                    {
                        String s = "Requires: ";
                        for(TowerType tt : Towers[i].requires)
                            s += "\n - " + tt.name;
                        tip.show(x, y, s);
                    }
                        
                }
            });
            p.addMouseListener(new java.awt.event.MouseAdapter()  {
                public void mouseExited(java.awt.event.MouseEvent e)
                {
                    tip.hide();
                }
            });
            Towers = t;
            Redraw();
        }
    }
    
    public double UIUpgradeX = 0.718,
                  UIUpgradeY = 0.93;
    public double UIUpgradeSizeX = 0.080,
                  UIUpgradeSizeY = 0.06;
    public double UISellX = 0.815,
                  UISellY = 0.817;
    public double UISellSizeX = 0.172,
                  UISellSizeY = 0.037;
    
    public double UICanvasX = 0.7;
    public double UICanvasY = 0.8;
    public double UIPicX = 0.72,
                  UIPicY = 0.82;
    
   int gcWidth, gcHeight, cellX, cellY;
    public void DrawUI(Graphics2D g, Point mPos)
    {
        if(SelectedObject == NOTHING || SelectedObject == TOWERTYPE)
            return;
        
        Color c = new Color(100, 100, 100, 150);
        g.setColor(c);
        g.fillRect((int)(gcWidth * UICanvasX), (int)(gcHeight * UICanvasY), 
                   (int)(gcWidth * (1-UICanvasX)), (int)(gcHeight * (1-UICanvasY)));
        
        if(SelectedObject == TOWER)
        {
            Point p = new Point((int)(gcWidth * UIPicX), (int)(gcHeight * UIPicY));
            DrawTower(selTower.getColor(), selTower.getSize(), g, p, cellX * 36/10 / selTower.getSize(), cellY * 36/10 / selTower.getSize(), null);
            TowerType tt = selTower.getType();
        }
        if(SelectedObject == MOB)
        {
            if(!selMob.isAlive())
            {
                SelectedObject = NOTHING;
                return;
            }
            DrawMob(selMob.getColor(), g, (int)((UICanvasX + 0.04) * gcWidth), (int)((UICanvasY + 0.04) * gcHeight), 8, 8);
            
            g.setColor(new Color(255, 200, 40));
            g.setFont(new Font("serif", Font.BOLD, 16));
            double nameX = UICanvasX + 0.075,
                nameY = UICanvasY + 0.047;
            g.drawString(selMob.getName(), (int)(nameX * gcWidth), (int)(nameY * gcHeight));
            
        }
        
    }
    
    TowerTooltip TTip = new TowerTooltip(this);
    
    class TowerTooltip
    {
        JFrame owner;
        JPanel p = new JPanel();
        JLabel lblName = new JLabel(),
               lblDmgA = new JLabel(),
               lblDmgB = new JLabel(),
               lblRangeA = new JLabel(),
               lblRangeB = new JLabel(),
               lblCdA = new JLabel(),
               lblCdB = new JLabel(),
               lblAura = new JLabel(),
               lblPrice = new JLabel();
        Canvas c = new Canvas();
        TowerType tt;
        int pw = 170,
            ph = 105;
        
        public TowerTooltip(JFrame o)
        {
            p.setBorder(BorderFactory.createLineBorder(Color.black));
            p.setBackground(new Color(70, 70, 70, 192));
            p.setLayout(null);
            p.setSize(pw ,ph);
            p.setVisible(false);
            owner = o;
            
            c = new Canvas();
            c.setSize(50, 50);
            c.setLocation(6, 20);
            p.add(c);
            
            
            //lblName.setLocation(66, 6);
            lblName.setForeground(Color.WHITE);
            lblName.setFont(new Font("serif", Font.BOLD, 12));
            p.add(lblName);
            
            Font f = new Font("dialog", Font.BOLD, 11);
            
            lblPrice.setForeground(new Color(255, 200, 40));
            lblPrice.setBounds(200, 200, 200, 200);
            lblPrice.setFont(new Font("dialog", Font.BOLD, 12));
            p.add(lblPrice);
            
            p.add(lblDmgA);
            lblDmgA.setLocation(66, 30);
            lblDmgA.setForeground(new Color(255, 200, 40));
            lblDmgA.setText("Damage: ");
            lblDmgA.setSize(lblDmgA.getPreferredSize());
            lblDmgA.setFont(f);
            
            p.add(lblDmgB);
            lblDmgB.setForeground(Color.WHITE);
            lblDmgB.setFont(f);
            
            p.add(lblRangeA);
            lblRangeA.setLocation(66, 48);
            lblRangeA.setForeground(new Color(255, 200, 40));
            lblRangeA.setText("Range: ");
            lblRangeA.setSize(lblRangeA.getPreferredSize());
            lblRangeA.setFont(f);
            
            p.add(lblRangeB);
            lblRangeB.setForeground(Color.WHITE);
            lblRangeB.setFont(f);
            
            p.add(lblCdA);
            lblCdA.setText("Cd: ");
            lblCdA.setSize(lblCdA.getPreferredSize());
            lblCdA.setLocation(66, 64);
            lblCdA.setForeground(new Color(255, 200, 40));
            lblCdA.setFont(f);
            
            p.add(lblAura);
            lblAura.setSize(100, 12);
            lblAura.setLocation(66, 80);
            lblAura.setForeground(new Color(255, 200, 40));
            lblAura.setFont(f);
            
            p.add(lblCdB);
            lblCdB.setForeground(Color.WHITE);
            lblCdB.setFont(f);
            o.add(p);
        }
        
        public void hide()
        {
            p.setVisible(false);
        }
        
        public void move(int x, int y)
        {
            x += 5;
            
            if(x + pw > owner.getWidth() - 9)
                x = owner.getWidth() - pw - 9;
            if(y + ph > owner.getHeight() - 23)
                y = owner.getHeight() - ph - 23;
            p.setLocation(x, y);
            p.setVisible(true);
        }
        
        public void show(TowerType t, int x, int y)
        {
            tt = t;
            
            Graphics g = c.getGraphics();
            DrawTower(t.color, t.size, (Graphics2D)g, new Point(0, 0), 50 / t.size, 50 / t.size,null);
            
            lblName.setText(t.name);
            lblName.setSize(lblName.getPreferredSize());
            lblName.setLocation(66 + (pw - 66 - 6 - lblName.getWidth())/2, 6);
            lblCdB.setText(String.valueOf((double)(t.Cooldown / 10) / 100));
            lblCdB.setSize(lblCdB.getPreferredSize());
            lblCdB.setLocation((pw - 12 - lblCdB.getWidth()), 64);
            lblDmgB.setText(t.MinDamage + " - " + t.MaxDamage);
            lblDmgB.setSize(lblDmgB.getPreferredSize());
            lblDmgB.setLocation((pw - 12 - lblDmgB.getWidth()), 30);
            lblRangeB.setText(t.Range + "");
            lblRangeB.setSize(lblRangeB.getPreferredSize());
            lblRangeB.setLocation((pw - 12 - lblRangeB.getWidth()), 48);
            
            lblPrice.setText(t.price + "G");
            lblPrice.setSize(lblPrice.getPreferredSize());
            lblPrice.setLocation((66 - lblPrice.getWidth()) / 2, 74);
            //lblPrice.setBounds(20, 48, 200, 20);
            
            if(t.tAura != null)
                lblAura.setText(t.tAura.name);
            if(t.mAura != null)
                lblAura.setText(t.mAura.name);
            move(x, y);
        }
    }
    
    
    
    GameTooltip Tooltip = new GameTooltip(this);
    class GameTooltip
    {
        JFrame owner;
        JPanel p = new JPanel();
        JTextArea lbl = new JTextArea();
        
        int pw = 175,
                  ph = 125;
        TowerType tower;
        public GameTooltip(JFrame o)
        {
            p.setBorder(BorderFactory.createLineBorder(Color.black));
            p.setBackground(new Color(70, 70, 70, 192));
            p.setVisible(false);
            p.setLayout(null);
            owner = o;
            
            lbl.setLocation(12, 12);
            lbl.setOpaque(false);
            lbl.setForeground(new Color(255, 200, 40));
            lbl.setSize(pw - 24, ph - 24);
            lbl.setWrapStyleWord(true);
            p.add(lbl);
            //lbl[6].setForeground(new Color(222, 222, 222));
            o.add(p);
        }
        
        public void move(int x, int y)
        {
            x += 5;
            
            if(x + pw > owner.getWidth() - 9)
                x = owner.getWidth() - pw - 9;
            if(y + ph > owner.getHeight() - 23)
                y = owner.getHeight() - ph - 23;
            p.setLocation(x, y);
            p.setVisible(true);
        }
        
        public void show(int x, int y, String s)
        {
            //tower = tt;
            lbl.setText(s);
            lbl.setSize(lbl.getPreferredSize());
            pw = lbl.getWidth() + 24;
            ph = lbl.getHeight() + 24;
            p.setSize(pw, ph);
            move(x, y);
        }
        
        public void hide()
        {
            p.setVisible(false);
        }
    }
    
    void DrawMob(Color c, Graphics2D g, int cx, int cy, int sx, int sy)
    {
        g.setColor(c);
        g.fillOval(cx - sx, cy - sy, sx*2, sy*2);
    }
    
    void DrawTower(Color c, int s, Graphics2D g, Point p, int cellX, int cellY, IMob tar)
    {
            int x = p.x,
                y = p.y;
            g.setColor(c);
            g.fillRoundRect(x, y, cellX * s, cellY * s, cellX * s * 2/5, cellY * s * 2/5);
            c = c.darker();
            g.setColor(c);
            g.fillRoundRect((int)(x + 0.25 * cellX), (int)(p.y + 0.25 * cellY),
                            cellX * s - cellX/2, cellY * s - cellY/2, (int)(cellX * (s-0.5) *2/5), (int)(cellY * (s-0.5) *2/5));
            c = c.darker();
            g.setColor(c);
            g.fillRoundRect((int)(p.x + 0.4 * cellX), (int)(p.y + 0.4 * cellY),
                            (int)(cellX * (s - 0.8)), (int)(cellY * (s - 0.8)), (int)(cellX * (s-0.8) *2/5), (int)(cellY * (s-0.8) *2/5));
            
            c = c.darker();
            g.setColor(c);
            //center x,y
            double cx = (double)p.x + (double)(s*cellX/2),
                   cy = (double)p.y + (double)(s*cellY/2);
            
            //draw midtower
            g.fillOval((int)(cx - 0.2 * s * cellX), 
                       (int)(cy - 0.2 * s * cellY),
                       (int)(0.4 * s * cellX), (int)(0.4 * s * cellY));
            //draw turret :D
            g.setColor(Color.black);
            if(tar == null)
                g.fillOval((int)(cx - 0.1 * s * cellX), 
                           (int)(cy - 0.1 * s * cellY),
                           (int)(0.2 * s * cellX), (int)(0.2 * s * cellY));
            else
            {
                IMob m = tar;
                Point2D mp = m.GetPosition();
                double ang = Math.atan2(mp.getY()*cellY - cy, mp.getX()*cellX - cx);
                cx += Math.cos(ang) * 0.15 * s * cellX;
                cy += Math.sin(ang) * 0.15 * s * cellY;
                g.fillOval((int)(cx - 0.1 * s * cellX), 
                           (int)(cy - 0.1 * s * cellY),
                           (int)(0.2 * s * cellX), (int)(0.2 * s * cellY));
            }
    }
    
    void SetCompPositions()
    {
        int w = GameCanvas.getWidth();
        pGold.setLocation(8, 5);
        pLives.setLocation((30 + pGold.getWidth()), 5);
        //pLives.setLocation((w - pLives.getWidth()) * 1/5, 0);
        pMobKills.setLocation((w - pMobKills.getWidth() - 4), 5);
        pMobsAlive.setLocation((pMobKills.getX() - pMobsAlive.getWidth() - 14), 5);
        lblLevelInfo.setLocation((w - lblLevelInfo.getWidth())/2, 5);
        if(TMenu != null) TMenu.Redraw();
        pTowers.setBounds(w + 24, 27, this.getWidth() - w - 40, GameCanvas.getHeight());
        
    }
    
    public MainForm() 
    {
        initComponents();
        
        CreateWinMenuUI();
        InitCanvas();
        SetCompPositions();
        
        pGameUIBar.removeAll();
        this.remove(pTowers);
        this.add(pTowers);
        pGameUIBar.add(lblLevelInfo);
        pGameUIBar.add(pGold);
        pGameUIBar.add(pMobsAlive);
        pGameUIBar.add(pMobKills);
        pGameUIBar.add(pLives);
        
        showWinUI(false);
        showScenarioUI(true);
        showGameUI(false);
        
    }
    
    Tile[][] CurrentMap;
    Game CurrentGame;
    Point MapSize;
    Scenario CurrentScenario;
    
    TowerType CurrentBrush = null;
    
    public void LoadTowers()
    {
        TowerType[] t = CurrentScenario.TowerList;
        for(int i = 0; i < t.length; i++)
        {
            
        }
        
    }
    
    public void onGameFinish()
    {
        
    }
    
    //starts a new game
    public void StartGame(Scenario s)
    {
        if(CurrentGame != null)
        {
            //NYI
            return;
        }
        
        //jPanel1.setLocation(0,0);
        //start a new game with specified scenario
        //NYI
        CurrentScenario = s;
        LoadTowers();
        CurrentMap = Map.LoadFromFile(CurrentScenario.MapFile, new Point(0,0), new Point(0,0), new ArrayList<Point>());
        MapSize = new Point(CurrentMap.length, CurrentMap[0].length);
        CurrentGame = new Game(CurrentScenario, 60);
        CurrentGame.SetOnUpdate(new onUpdateHandler());
        CurrentGame.setOnGameFinish(new Runnable()
        {
            public void run()
            {
                onGameFinish();
            }
        });
        TMenu = new TowerMenu(CurrentScenario.TowerList, pTowers);
        Console.AddLine("=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-");
        Console.AddLine("Loaded scenario: '" + CurrentScenario.Name + "'");
        Console.AddLine("");
        Console.AddLine("You have " + (int)(CurrentGame.GetTimeToWave()/1000) + " seconds to prepare!");
        Console.AddLine("=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-");
        lblMobKills.setText("  0");
        formComponentResized(null);
    }
    
    BufferedImage BackgroundImg;
    
    public Color GetTileColor(Tile t)
    {
        if(t.canWalk)
            return CurrentScenario.TileColor_Walkable;
        else
            return CurrentScenario.TileColor_Unwalkable;
    }
    
    public void DrawBackgroundImage(Graphics2D g)
    {
        
        int height = GameCanvas.getHeight();
        int width = GameCanvas.getWidth();
        if(BackgroundImg == null || BackgroundImg.getHeight() != GameCanvas.getHeight()
                                 || BackgroundImg.getWidth() != GameCanvas.getWidth())
        {
            BackgroundImg = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
            Graphics gBg = BackgroundImg.getGraphics();
            
            int ch = height / MapSize.y;
            int cw = width / MapSize.x;
            int t = 0;
            for(int y = 0; y < MapSize.y; y++)
                for(int x = 0; x < MapSize.x; x++)
                {
                    Color c = GetTileColor(CurrentMap[x][y]);
                    
                    //gBg.setColor(c);
                    //gBg.fillRect(x*cw, y*ch, cw, ch);
                    Tile[][] m = CurrentMap;
                    for(int cx = -1; cx < 2; cx+=2)
                        for(int cy = -1; cy < 2; cy+=2)
                        {
                            int drawx = x*cw + (cx+1)*cw/4,
                                drawy = y*ch + (cy+1)*ch/4;
                            if(x+cx < 0 || y+cy < 0 || x+cx >= MapSize.x || y+cy >= MapSize.y)
                            {
                                gBg.setColor(c);
                                gBg.fillRect(drawx, drawy, cw/2, ch/2);
                                continue;
                            }
                                if(m[x+cx][y] == m[x][y+cy] && m[x][y+cy] != m[x][y]
                                   && m[x+cx][y+cy] != m[x][y])
                                {
                                    gBg.setColor(GetTileColor(m[x][y+cy]));
                                    gBg.fillRect(drawx, drawy, cw/2, ch/2);
                                    
                                    gBg.setColor(c);
                                    int ang;
                                    if(cx == -1)
                                        if(cy == -1)
                                            ang = 1;
                                        else
                                            ang = 2;
                                    else
                                        if(cy == -1)
                                            ang = 0;
                                        else
                                            ang = 3;
                                    gBg.fillArc(x*cw, y*ch, cw, ch, ang*90, 90);
                                }
                                else
                                {
                                    gBg.setColor(c);
                                    gBg.fillRect(drawx, drawy, cw/2, ch/2);
                                }
                        }
                    
                }
        }
        g.drawImage(BackgroundImg, 0, 0, width, height, null);
    }
    
    GameState oldState = GameState.BetweenWaves;
    long clearTime;
    int clearLeaks;
    //draws all the stuff on the screen
    public void onUpdate(Graphics2D g)
    {
        DrawBackgroundImage(g);
        IMob[] mobs = CurrentGame.GetMobs();
        int mobsc = CurrentGame.GetMobsCount();
        for(int i = 0; i < mobsc; i++)
        {
            Point2D p = mobs[i].GetPosition();
            int cx = (int)(p.getX() * cellX),
                cy = (int)(p.getY() * cellY);
            if(!mobs[i].isAlive())
            {
                double dec = mobs[i].getDecayTime() / IMob.DECAYTIME;
                Color c = new Color(mobs[i].getColor().getRed(), mobs[i].getColor().getGreen(), mobs[i].getColor().getBlue(), (int)(dec*255));
                DrawMob(c, g, cx, cy, (int)(0.4 * cellX), (int)(0.4 * cellY));
            }
        }
        for(int i = 0; i < mobsc; i++)
        {
            Point2D p = mobs[i].GetPosition();
            int cx = (int)(p.getX() * cellX),
                cy = (int)(p.getY() * cellY);
            if(mobs[i].isAlive())
            {
                g.setStroke(new BasicStroke(1));
                Color c = mobs[i].getColor();
                DrawMob(c, g, cx, cy, (int)(0.4 * cellX), (int)(0.4 * cellY));
            }
        }
        ITower[] Towers = CurrentGame.GetTowers();
        int tCount = CurrentGame.GetTowersCount();
        for(int i = 0; i < tCount; i++)
        {
            ITower t = Towers[i];
            Point p = t.getPosition();
            int s = t.getSize();
            Color c = t.getColor();
            DrawTower(c, t.getSize(), g, new Point(p.x * cellX, p.y*cellY), cellX, cellY, t.getTarget());
        }
        IMissile[] Missiles = CurrentGame.GetMissiles();
        int mCount = CurrentGame.GetMissilesCount();
        for(int i = 0; i < mCount; i++)
        {
            IMissile m = Missiles[i];
            double o = m.GetOrientation();
            Point2D p = m.GetPosition();
            double x = p.getX(),
                   y = p.getY();
            double tipX = x + Math.cos(o) * 0.2,
                   tipY = y + Math.sin(o) * 0.2,
                   endX = x - Math.cos(o) * 0.2,
                   endY = y - Math.sin(o) * 0.2,
                   aX = tipX + Math.cos(o + Math.PI * 0.8) * 0.2,
                   aY = tipY + Math.sin(o + Math.PI * 0.8) * 0.2,
                   bX = tipX + Math.cos(o - Math.PI * 0.8) * 0.2,
                   bY = tipY + Math.sin(o - Math.PI * 0.8) * 0.2;
            
            g.setColor(m.GetColor());
            
            g.setStroke(new BasicStroke(2));
            g.drawLine((int)(tipX * cellX), (int)(tipY * cellY), (int)(endX * cellX), (int)(endY * cellY));
            g.drawLine((int)(tipX * cellX), (int)(tipY * cellY), (int)(aX * cellX), (int)(aY * cellY));
            g.drawLine((int)(tipX * cellX), (int)(tipY * cellY), (int)(bX * cellX), (int)(bY * cellY));
            g.setStroke(new BasicStroke(1));
        }
        
        lblGold.setText(f(5, CurrentGame.GetCurrentGold()));
        lblMobKills.setText(f(3, CurrentGame.GetKills()));
        if(CurrentGame.GetCurrentState() == GameState.InWave)
        {
            lblMobs.setText(f(2, CurrentGame.GetMobsAlive()) + "/" + f(2, CurrentScenario.MobCount[CurrentGame.GetCurrentLevel()]));
            lblLives.setText(f(2, CurrentGame.GetLives()));
        }        
        else
        {
            lblMobs.setText("-/-");
            g.setColor(new Color(211, 0, 0));
            g.setStroke(new BasicStroke(2));
            Font f = new Font("sansserif", Font.BOLD, 22);
            g.setFont(f);
            String s;
            int clvl = CurrentGame.GetCurrentLevel();
            if(clvl == -1)
                s = "First wave starts in ";
            else
                s = "Wave " + (clvl+2) + " (" + CurrentScenario.MobList[clvl+1].name + ") starts in ";
            s += (int)(CurrentGame.GetTimeToWave() / 1000) + " sec";
            int l = GetStringPixelLen(s, f);
            g.drawString(s, (gcWidth-l)/2, 50);
        }
        lblLives.setText(String.valueOf(CurrentGame.GetLives()));
        if(oldState != CurrentGame.GetCurrentState())
        {
            oldState = CurrentGame.GetCurrentState();
            if(CurrentGame.GetCurrentState() == GameState.BetweenWaves)
            {
                long t = System.currentTimeMillis() - clearTime;
                int leaks = clearLeaks - CurrentGame.GetLives();
                t /= 100;
                lblLevelInfo.setText("Preparing for level " + (CurrentGame.GetCurrentLevel()+2));
                Console.AddLine("Level " + (CurrentGame.GetCurrentLevel()+1) + " cleared! (" + (double)t/10 + " sec, " + leaks + " leaks!)");
            }
            else
            {
                int l = CurrentGame.GetCurrentLevel();
                lblLevelInfo.setText("Level " + (CurrentGame.GetCurrentLevel()+1) +" (" + CurrentScenario.MobList[l].name + ")");
                Console.AddLine("Level " + (l+1) + " (" + CurrentScenario.MobList[l].name + ") starting!");
                clearLeaks = CurrentGame.GetLives();
                clearTime = System.currentTimeMillis();
            }
            lblLevelInfo.setSize(lblLevelInfo.getPreferredSize());
        lblLevelInfo.setLocation((GameCanvas.getWidth() - lblLevelInfo.getWidth())/2, 5);
        }
        //Console.Print(g);
    }
    
    String f(int i, int tf)
    {
        int l = (int)(Math.log10(tf));
        String r = "";
        for(int k = 1; k < i - l; k++)
            r += " ";
        r += String.valueOf(tf);
        return r;
    }
    int GetStringPixelLen(String s, Font f)
    {
        return this.getFontMetrics(f).stringWidth(s);
    }
    
    int SelectedObject;
    final static int NOTHING = 0;
    final static int TOWERTYPE = 1;
    final static int MOB = 2;
    final static int TOWER = 3;
    
    IMob selMob;
    ITower selTower;
    public void GetEntityAtXY(int x, int y)
    {
        Point2D p = new Point2D.Double((double)x / GameCanvas.getWidth() * MapSize.x,
                                       (double)y / GameCanvas.getHeight() * MapSize.y);
        selMob = null;
        selTower = null;
        int mC = CurrentGame.GetMobsCount();
        IMob[] m = CurrentGame.GetMobs();
        double d;
        for(int i = 0; i < mC; i++)
        {
            if(m[i].isAlive())
            {
                d = Pathfinding.GetDist2(p, m[i].GetPosition());
                if(d <= 0.45 * 0.45)
                {
                    selMob = m[i];
                    return;
                }
            }
        }
        int tC = CurrentGame.GetTowersCount();
        ITower[] t = CurrentGame.GetTowers();
        int cx = (int)p.getX(),
            cy = (int)p.getY();
        for(int i = 0; i < tC; i++)
        {
            Point tp = t[i].getPosition();
            int s = t[i].getSize();
            if(tp.x <= cx && tp.y <= cy && tp.x + s > cx && tp.y + s > cy)
            {
                selTower = t[i];
                return;
            }
        }
    }
    
    public void onCanvasClick(int x, int y, int Button)
    {
        int gcWidth = GameCanvas.getWidth(),
            gcHeight = GameCanvas.getHeight(),
            cellX = gcWidth / MapSize.x,
            cellY = gcHeight / MapSize.y;
        if(TMenu.selected != -1)
        {
            if(Button == MouseEvent.BUTTON2)
            {
                SelectedObject = NOTHING;
                TMenu.selected = -1;
                TMenu.Redraw();
                return;
            }
            else
            {
                TowerType tt = CurrentScenario.TowerList[TMenu.selected];
                int tx = (int)((double)x / (double)cellX - (double)tt.size / 2 + 0.5);
                int ty = (int)((double)y / (double)cellY - (double)tt.size / 2 + 0.5);
                if(tx < 0)
                    tx = 0;
                if(ty < 0)
                    ty = 0;
                if(ty > MapSize.y - tt.size)
                    ty = MapSize.y - tt.size;
                if(tx > MapSize.x - tt.size)
                    tx = MapSize.x - tt.size;
                if(CurrentGame.CanBuildTower(tt.size, tx, ty))
                {
                    CurrentGame.BuildTower(tt, tx, ty);
                    return;
                }
            }
        }
        SelectedObject = NOTHING;
        TMenu.selected = -1;
        TMenu.Redraw();
        GetEntityAtXY(x, y);
        if(selMob != null)
        {
            SelectedObject = MOB;
            btnSell.setVisible(false);
            btnUpgr.setVisible(false);
        }
        else if(selTower != null)
        {
            SelectedObject = TOWER;
            if(selTower.getUpgrade() != null)
                btnUpgr.setVisible(true);
            btnSell.setVisible(true);
        }
        else
        {
            SelectedObject = NOTHING;
            btnSell.setVisible(false);
            btnUpgr.setVisible(false);
        }
        
    }
    
    Button btnUpgr = new Button();
    Button btnSell = new Button();
    
    public void showGameUI(boolean b)
    {
        //btnUpgr.setVisible(b);
        //btnSell.setVisible(b);
        GameCanvas.setVisible(b);
        pGameUIBar.setVisible(b);
        pTowers.setVisible(b);
    }
    
    JPanel winUI = new JPanel(),
           scenarioUI = new JPanel();
    JComboBox<String> cbScenario = new JComboBox<String>();
    JButton btnStartGame = new JButton();
    
    public void showScenarioUI(boolean show)
    {
        scenarioUI.setVisible(show);
    }
    
    public void showWinUI(boolean show)
    {
        winUI.setVisible(show);
    }
    
    public void CreateWinMenuUI()
    {
        scenarioUI.setBounds((getWidth() - 250)/2, (getWidth()-150)/3, 250, 130);
        cbScenario.addItem("Default");
        JLabel l = new JLabel("Select a scenario:");
        l.setFont(new Font("dialog", Font.BOLD, 22));
        l.setForeground(new Color(255, 200, 40));
        l.setSize(l.getPreferredSize());
        l.setLocation(35, 12);
        scenarioUI.add(l);
        cbScenario.setBounds(25, 50, 200, 20);
        scenarioUI.add(cbScenario);
        scenarioUI.setLayout(null);
        scenarioUI.setBackground(new Color(70, 70, 70));
        
        scenarioUI.add(btnStartGame);
        btnStartGame.setBounds(25, 76, 200, 30);
        btnStartGame.setText("Start!");
        btnStartGame.addMouseListener(new java.awt.event.MouseAdapter() {
            @Override
            public void mousePressed(java.awt.event.MouseEvent e)
            {
                showGameUI(true);
                showScenarioUI(false);
                StartGame(GameData.Default.Scenario);
            }
            });
        
        
        add(scenarioUI);
        winUI.setBounds((getWidth() - 350)/2, (getWidth()-120)/3, 350, 120);
        add(winUI);
        
    }
    
    public boolean CanvasInitialized = false;
    //initializes the canvas (game area)
    public void InitCanvas()
    {
        if(CanvasInitialized)
            return;
        CanvasInitialized = true;
        btnUpgr.setVisible(false);
        btnSell.setVisible(false);
        btnUpgr.setBackground(new Color(150, 150, 150));
        btnSell.setBackground(new Color(150, 150, 150));
        btnUpgr.setLabel("Upgrade");
        btnSell.setLabel("Sell");
        btnUpgr.addMouseListener(new java.awt.event.MouseAdapter() {
            @Override
            public void mousePressed(java.awt.event.MouseEvent e)
            {
                selTower = CurrentGame.UpgradeTower(selTower);
                btnUpgr.setVisible(selTower.getType().upgradesTo != null);
            }
            @Override
            public void mouseExited(java.awt.event.MouseEvent e)
            {
                TTip.hide();
            }
        });
        btnUpgr.addMouseMotionListener(new java.awt.event.MouseMotionAdapter() {
            @Override
            public void mouseMoved(java.awt.event.MouseEvent e)
            {
                TTip.show(selTower.getType().upgradesTo, btnUpgr.getX(),
                                                         btnUpgr.getY() - TTip.ph - 6);
            }
        });
    
        btnSell.addMouseListener(new java.awt.event.MouseAdapter() {
            @Override
            public void mousePressed(java.awt.event.MouseEvent e)
            {
                CurrentGame.DestroyTower((Tower)selTower, true);
                selTower = null;
                SelectedObject = NOTHING;
                btnSell.setVisible(false);
                btnUpgr.setVisible(false);
            }
        });
        this.add(btnUpgr);
        this.add(btnSell);
        getContentPane().setBackground(Color.black);
        GameCanvas = new Canvas();
        GameCanvas.addMouseListener(new java.awt.event.MouseAdapter() {
            @Override
            public void mousePressed(java.awt.event.MouseEvent e)
            {
                onCanvasClick(e.getX(), e.getY(), e.getButton());
            }
        });
        GameCanvas.setLocation(12, 27);
        GameCanvas.setSize(this.getWidth() - CanvasDifferenceX, this.getHeight() - CanvasDifferenceY);
        //formComponentResized(null);
        this.add(GameCanvas);
        
        //doing manual repaint, so no auto
        //GameCanvas.setIgnoreRepaint(true);
        //creates a double buffer
        GameCanvas.createBufferStrategy(2);
        CanvasBS = GameCanvas.getBufferStrategy();

        //get compatible image info
        GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
        GraphicsDevice gd = ge.getDefaultScreenDevice();
        gc = gd.getDefaultConfiguration();
        
        //create buffer image
        CanvasBuffer = gc.createCompatibleImage(GameCanvas.getWidth(), GameCanvas.getHeight());
        gBuffer = CanvasBuffer.createGraphics(); //buffer graphics
        
    }
    
    int CanvasDifferenceX = 225;
    int CanvasDifferenceY = 60;
    
    
    Canvas GameCanvas;
    
    BufferStrategy CanvasBS;
    BufferedImage CanvasBuffer;
    GraphicsConfiguration gc;
    Graphics2D gBuffer;
    Graphics gCanvas;
    
    //handles updating and drawing to screen
    public class onUpdateHandler implements Updatable
    {
        public void onUpdate()
        {
                //should we resize the buffer image
                if(CanvasBuffer.getHeight() != GameCanvas.getHeight() || CanvasBuffer.getWidth() != GameCanvas.getWidth())
                {
                    gBuffer.dispose();
                    CanvasBuffer = gc.createCompatibleImage(GameCanvas.getWidth(), GameCanvas.getHeight());
                    gBuffer = CanvasBuffer.createGraphics();
                }
            
                //draw on the buffer
                Graphics2D gb = (Graphics2D)gBuffer;
                gb.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
                
                //draw main window
                MainForm.this.onUpdate(gb);
                
                //draw overlay
                Point p = GameCanvas.getMousePosition();
                MainForm.this.DrawOverlay(gb, p);
                MainForm.this.DrawUI(gb, p);
                
                Console.Print(gb);        
                //put it on the screen
                gCanvas = CanvasBS.getDrawGraphics();
                gCanvas.drawImage(CanvasBuffer, 0, 0, null);
                if(!CanvasBS.contentsLost())
                    CanvasBS.show();
                gCanvas.dispose();
                
        }
    }
    
    double OverlayDarken = 1;
    boolean OverlayRise = false;
    void DrawOverlay(Graphics2D g, Point mPos)
    {
        int gcWidth = GameCanvas.getWidth(),
            gcHeight = GameCanvas.getHeight(),
            cellX = gcWidth / MapSize.x,
            cellY = gcHeight / MapSize.y;
        if(OverlayRise)
        {
            OverlayDarken += 0.005;
            if(OverlayDarken >= 1)
                OverlayRise = false;
        }
        else   
        {
            OverlayDarken -= 0.005;
            if(OverlayDarken <= 0.65)
                OverlayRise = true;
        }
        Color border = new Color((int)(200 * OverlayDarken), 
                                 (int)(200 * OverlayDarken), 
                                 (int)(255 * OverlayDarken));
        switch(SelectedObject)
        {
            case NOTHING:
                break;
            case TOWERTYPE:
                if(TMenu == null || TMenu.selected == -1)
                {
                    Console.AddLine("ERROR");
                    break;
                }
                boolean[][] bMap = CurrentGame.GetTowerMap();
                g.setStroke(new BasicStroke(1));
                g.setColor(Color.black);
                for(int x = 0; x < MapSize.x; x++)
                    for(int y = 0; y < MapSize.y; y++)
                    {
                        if(bMap[x][y])
                        {
                            g.drawLine(x * cellX, y * cellY, x * cellX, (int)(((double)y + 0.2) * cellY));
                            g.drawLine(x * cellX, y * cellY, (int)(((double)x + 0.2) * cellX), y * cellY);
                            g.drawLine((x+1) * cellX, (y+1) * cellY, (x+1) * cellX, (int)(((double)y - 0.2 + 1) * cellY));
                            g.drawLine((x+1) * cellX, (y+1) * cellY, (int)(((double)x - 0.2 + 1) * cellX), (y+1) * cellY);
                            g.drawLine((x+1) * cellX, (y) * cellY, (x+1) * cellX, (int)(((double)y + 0.2) * cellY));
                            g.drawLine((x+1) * cellX, (y) * cellY, (int)(((double)x - 0.2 + 1) * cellX), (y) * cellY);
                            g.drawLine((x) * cellX, (y+1) * cellY, (x) * cellX, (int)(((double)y - 0.2 + 1) * cellY));
                            g.drawLine((x) * cellX, (y+1) * cellY, (int)(((double)x + 0.2) * cellX), (y+1) * cellY);
                        }
                    }
                
                if(mPos == null)
                    break;
                TowerType tt;
                if(TMenu.selected == -1)
                    return;
                tt = CurrentScenario.TowerList[TMenu.selected];
                int bX = (int)((double)mPos.x / (double)cellX - (double)tt.size / 2 + 0.5);
                int bY = (int)((double)mPos.y / (double)cellY - (double)tt.size / 2 + 0.5);
                if(bX < 0)
                    bX = 0;
                if(bY < 0)
                    bY = 0;
                if(bX + tt.size >= MapSize.x)
                    bX = MapSize.x - tt.size;
                if(bY + tt.size >= MapSize.y)
                    bY = MapSize.y - tt.size;
                if(CurrentGame.CanBuildTower(tt.size, bX, bY))
                {
                    if(CurrentGame.GetCurrentGold() >= tt.price)
                        g.setColor(new Color(200, 200, 255, 127));
                    else
                        g.setColor(new Color(222, 0, 0, 192));
                g.fillRect(bX * cellX, bY * cellY, cellX * tt.size, cellY * tt.size);
                }
                break;
            case TOWER:
                Point tp = selTower.getPosition();
                int s = selTower.getSize();
                double r = 0;
                int tx = (int)(tp.x + (double)selTower.getSize() / 2);
                int ty = (int)(tp.y + (double)selTower.getSize() / 2);
                if(selTower.getType().mAura != null)
                {
                    r = selTower.getType().mAura.Range;
                    g.setColor(new Color(255, 50, 50, 100));
                    g.fillOval((int)(tx-r)*cellX, (int)(ty-r)*cellY, (int)(2*r)*cellX, (int)(2*r)*cellY);
                }
                else if(selTower.getType().tAura != null)
                {
                    r = selTower.getType().tAura.Range;
                    g.setColor(new Color(50, 50, 255, 100));
                    g.fillOval((int)(tx-r)*cellX, (int)(ty-r)*cellY, (int)(2*r)*cellX, (int)(2*r)*cellY);
                }
                g.setStroke(new BasicStroke(3));
                g.setColor(border);
                g.drawRoundRect(tp.x * cellX - 1, tp.y * cellY - 1, s * cellX + 1, s * cellY + 1, s * cellX * 2/5, s * cellY * 2/5);
                break;
            case MOB:
                Point2D mp = selMob.GetPosition();
                g.setStroke(new BasicStroke(2));
                g.setColor(border);
                g.drawOval((int)((mp.getX()-0.4)*(double)cellX), 
                           (int)((mp.getY()-0.4)*(double)cellY), 
                           cellX*8/10 , 
                           cellY*8/10 );
                break;
        }
        
        int mobsc = CurrentGame.GetMobsCount();
        IMob[] mobs = CurrentGame.GetMobs();
        for(int i = 0; i < mobsc; i++)
        {
            Point2D p = mobs[i].GetPosition();
            int cx = (int)(p.getX() * cellX),
                cy = (int)(p.getY() * cellY);
            if(mobs[i].isAlive())
            {
                g.setStroke(new BasicStroke(1));
                cx -= cellX;
                cy -= 10 + cellY*0.5;
                int hp = mobs[i].GetCurLife();
                double perc = (double)hp / (double)mobs[i].GetMaxLife();
                g.setColor(Color.black);
                g.drawRect(cx, cy, cellX*2, 8);
                g.setColor(Color.red);
                g.fillRect(cx+1, cy+1, (int)((double)(cellX*2-1) * perc), 7);
            }
        }
        
        
    }
    
    GameConsole Console = new GameConsole();
    class GameConsole
    {
        int Duration = 10000;
        LinkedList<String> Lines = new LinkedList<String>();
        LinkedList<Long> LDuration = new LinkedList<Long>();
        void AddLine(String s)
        {
            Lines.addFirst(s);
            LDuration.addFirst(System.currentTimeMillis() + Duration);
        }
        
        void Print(Graphics2D g)
        {
            long t = System.currentTimeMillis();
            if(LDuration.size() <= 0)
                return;
            while(LDuration.size() > 0 && LDuration.peekLast() < t)
            {
                LDuration.removeLast();
                Lines.removeLast();
            }
            if(Lines.size() == 0)
                return;
            String[] lA = Lines.toArray(new String[1]);
            Long[] lD = LDuration.toArray(new Long[1]);
            g.setFont(new Font("sansserif", Font.BOLD, 12));
            for(int i = 0; i < lA.length; i++)
            {
                String s = lA[i];
                int tr = (int)(lD[i] - t);
                if(tr * 2 < Duration)
                    g.setColor(new Color(0, 0, 0, 255 * 2 * tr / Duration));
                else
                    g.setColor(Color.BLACK);
                g.drawString(s, 12-1, GameCanvas.getHeight() - (i) * 12-1-12);
                g.drawString(s, 12-1, GameCanvas.getHeight() - (i) * 12+1-12);
                g.drawString(s, 12+1, GameCanvas.getHeight() - (i) * 12-1-12);
                g.drawString(s, 12+1, GameCanvas.getHeight() - (i) * 12+1-12);
                if(tr * 2 < Duration)
                    g.setColor(new Color(255, 255, 255, 255 * 2 * tr / Duration));
                else
                    g.setColor(Color.WHITE);
                g.drawString(s, 12, GameCanvas.getHeight() - (i) * 12-12);
            }
        }
    }
    
    
    
    
    
    /**
     * This method is called from within the constructor to initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is always
     * regenerated by the Form Editor.
     */
    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {

        pGameUIBar = new javax.swing.JPanel();
        pMobKills = new javax.swing.JPanel();
        jLabel5 = new javax.swing.JLabel();
        lblMobKills = new javax.swing.JLabel();
        pMobsAlive = new javax.swing.JPanel();
        jLabel4 = new javax.swing.JLabel();
        lblMobs = new javax.swing.JLabel();
        pGold = new javax.swing.JPanel();
        jLabel1 = new javax.swing.JLabel();
        lblGold = new javax.swing.JLabel();
        pLives = new javax.swing.JPanel();
        jLabel3 = new javax.swing.JLabel();
        lblLives = new javax.swing.JLabel();
        lblLevelInfo = new javax.swing.JLabel();
        pTowers = new javax.swing.JLabel();

        setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
        setTitle("TD Platform");
        setBackground(new java.awt.Color(153, 255, 153));
        setMinimumSize(new java.awt.Dimension(800, 600));
        setName("MainUI");
        addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseClicked(java.awt.event.MouseEvent evt) {
                formMouseClicked(evt);
            }
        });
        addComponentListener(new java.awt.event.ComponentAdapter() {
            public void componentResized(java.awt.event.ComponentEvent evt) {
                formComponentResized(evt);
            }
        });
        addMouseMotionListener(new java.awt.event.MouseMotionAdapter() {
            public void mouseMoved(java.awt.event.MouseEvent evt) {
                formMouseMoved(evt);
            }
        });
        addWindowStateListener(new java.awt.event.WindowStateListener() {
            public void windowStateChanged(java.awt.event.WindowEvent evt) {
                formWindowStateChanged(evt);
            }
        });
        addKeyListener(new java.awt.event.KeyAdapter() {
            public void keyPressed(java.awt.event.KeyEvent evt) {
                formKeyPressed(evt);
            }
        });

        pGameUIBar.setBackground(new java.awt.Color(0, 0, 0));
        pGameUIBar.setName("TopBar");
        pGameUIBar.setPreferredSize(new java.awt.Dimension(283, 22));
        pGameUIBar.addComponentListener(new java.awt.event.ComponentAdapter() {
            public void componentResized(java.awt.event.ComponentEvent evt) {
                pGameUIBarComponentResized(evt);
            }
        });

        pMobKills.setOpaque(false);

        jLabel5.setFont(new java.awt.Font("Comic Sans MS", 1, 11)); // NOI18N
        jLabel5.setForeground(new java.awt.Color(204, 0, 0));
        jLabel5.setText("Killed:");
        jLabel5.setAlignmentX(1.0F);
        jLabel5.setAlignmentY(2.0F);

        lblMobKills.setBackground(new java.awt.Color(51, 51, 51));
        lblMobKills.setFont(new java.awt.Font("Courier New", 1, 12)); // NOI18N
        lblMobKills.setForeground(new java.awt.Color(255, 255, 255));
        lblMobKills.setHorizontalAlignment(javax.swing.SwingConstants.RIGHT);
        lblMobKills.setText("  0");
        lblMobKills.setHorizontalTextPosition(javax.swing.SwingConstants.RIGHT);
        lblMobKills.setName("lblGold");
        lblMobKills.setOpaque(true);
        lblMobKills.setRequestFocusEnabled(false);

        javax.swing.GroupLayout pMobKillsLayout = new javax.swing.GroupLayout(pMobKills);
        pMobKills.setLayout(pMobKillsLayout);
        pMobKillsLayout.setHorizontalGroup(
            pMobKillsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(pMobKillsLayout.createSequentialGroup()
                .addComponent(jLabel5)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(lblMobKills))
        );
        pMobKillsLayout.setVerticalGroup(
            pMobKillsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(pMobKillsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                .addComponent(jLabel5)
                .addComponent(lblMobKills))
        );

        pMobsAlive.setOpaque(false);

        jLabel4.setFont(new java.awt.Font("Comic Sans MS", 1, 11)); // NOI18N
        jLabel4.setForeground(new java.awt.Color(204, 0, 0));
        jLabel4.setText("Mobs Alive:");
        jLabel4.setAlignmentX(1.0F);
        jLabel4.setAlignmentY(2.0F);

        lblMobs.setBackground(new java.awt.Color(51, 51, 51));
        lblMobs.setFont(new java.awt.Font("Courier New", 1, 12)); // NOI18N
        lblMobs.setForeground(new java.awt.Color(255, 255, 255));
        lblMobs.setHorizontalAlignment(javax.swing.SwingConstants.RIGHT);
        lblMobs.setText("15/30");
        lblMobs.setHorizontalTextPosition(javax.swing.SwingConstants.RIGHT);
        lblMobs.setName("lblGold");
        lblMobs.setOpaque(true);
        lblMobs.setRequestFocusEnabled(false);

        javax.swing.GroupLayout pMobsAliveLayout = new javax.swing.GroupLayout(pMobsAlive);
        pMobsAlive.setLayout(pMobsAliveLayout);
        pMobsAliveLayout.setHorizontalGroup(
            pMobsAliveLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(pMobsAliveLayout.createSequentialGroup()
                .addComponent(jLabel4)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(lblMobs)
                .addGap(0, 4, Short.MAX_VALUE))
        );
        pMobsAliveLayout.setVerticalGroup(
            pMobsAliveLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(pMobsAliveLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                .addComponent(jLabel4)
                .addComponent(lblMobs))
        );

        pGold.setOpaque(false);

        jLabel1.setFont(new java.awt.Font("Comic Sans MS", 1, 11)); // NOI18N
        jLabel1.setForeground(new java.awt.Color(230, 180, 0));
        jLabel1.setText("Gold:");

        lblGold.setBackground(new java.awt.Color(51, 51, 51));
        lblGold.setFont(new java.awt.Font("Courier New", 1, 12)); // NOI18N
        lblGold.setForeground(new java.awt.Color(255, 255, 255));
        lblGold.setHorizontalAlignment(javax.swing.SwingConstants.RIGHT);
        lblGold.setText("1500");
        lblGold.setHorizontalTextPosition(javax.swing.SwingConstants.RIGHT);
        lblGold.setName("lblGold");
        lblGold.setOpaque(true);
        lblGold.setRequestFocusEnabled(false);

        javax.swing.GroupLayout pGoldLayout = new javax.swing.GroupLayout(pGold);
        pGold.setLayout(pGoldLayout);
        pGoldLayout.setHorizontalGroup(
            pGoldLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(pGoldLayout.createSequentialGroup()
                .addComponent(jLabel1)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(lblGold, javax.swing.GroupLayout.PREFERRED_SIZE, 40, javax.swing.GroupLayout.PREFERRED_SIZE))
        );
        pGoldLayout.setVerticalGroup(
            pGoldLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, pGoldLayout.createSequentialGroup()
                .addGap(0, 0, Short.MAX_VALUE)
                .addGroup(pGoldLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(jLabel1)
                    .addComponent(lblGold)))
        );

        pLives.setOpaque(false);

        jLabel3.setFont(new java.awt.Font("Comic Sans MS", 1, 11)); // NOI18N
        jLabel3.setForeground(new java.awt.Color(0, 204, 0));
        jLabel3.setText("Lives:");

        lblLives.setBackground(new java.awt.Color(51, 51, 51));
        lblLives.setFont(new java.awt.Font("Courier New", 1, 12)); // NOI18N
        lblLives.setForeground(new java.awt.Color(255, 255, 255));
        lblLives.setHorizontalAlignment(javax.swing.SwingConstants.RIGHT);
        lblLives.setText("30");
        lblLives.setHorizontalTextPosition(javax.swing.SwingConstants.RIGHT);
        lblLives.setName("lblGold");
        lblLives.setOpaque(true);

        javax.swing.GroupLayout pLivesLayout = new javax.swing.GroupLayout(pLives);
        pLives.setLayout(pLivesLayout);
        pLivesLayout.setHorizontalGroup(
            pLivesLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(pLivesLayout.createSequentialGroup()
                .addComponent(jLabel3)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(lblLives, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE))
        );
        pLivesLayout.setVerticalGroup(
            pLivesLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(pLivesLayout.createSequentialGroup()
                .addGroup(pLivesLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(jLabel3)
                    .addComponent(lblLives))
                .addGap(0, 0, Short.MAX_VALUE))
        );

        lblLevelInfo.setFont(new java.awt.Font("Comic Sans MS", 1, 12)); // NOI18N
        lblLevelInfo.setForeground(new java.awt.Color(102, 102, 255));
        lblLevelInfo.setText("Level 1");

        javax.swing.GroupLayout pGameUIBarLayout = new javax.swing.GroupLayout(pGameUIBar);
        pGameUIBar.setLayout(pGameUIBarLayout);
        pGameUIBarLayout.setHorizontalGroup(
            pGameUIBarLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(pGameUIBarLayout.createSequentialGroup()
                .addContainerGap()
                .addComponent(pGold, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addGap(42, 42, 42)
                .addComponent(pMobsAlive, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addGap(29, 29, 29)
                .addComponent(lblLevelInfo, javax.swing.GroupLayout.PREFERRED_SIZE, 91, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 44, Short.MAX_VALUE)
                .addComponent(pMobKills, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addGap(54, 54, 54)
                .addComponent(pLives, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addGap(246, 246, 246))
        );
        pGameUIBarLayout.setVerticalGroup(
            pGameUIBarLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(pGameUIBarLayout.createSequentialGroup()
                .addGap(1, 1, 1)
                .addGroup(pGameUIBarLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addComponent(lblLevelInfo)
                    .addComponent(pLives, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(pGold, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(pMobsAlive, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(pMobKills, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
        );

        pTowers.setBackground(new java.awt.Color(51, 51, 51));
        pTowers.setOpaque(true);

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
        getContentPane().setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addContainerGap()
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addGroup(layout.createSequentialGroup()
                        .addComponent(pGameUIBar, javax.swing.GroupLayout.DEFAULT_SIZE, 817, Short.MAX_VALUE)
                        .addContainerGap())
                    .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
                        .addGap(0, 0, Short.MAX_VALUE)
                        .addComponent(pTowers, javax.swing.GroupLayout.PREFERRED_SIZE, 127, javax.swing.GroupLayout.PREFERRED_SIZE)
                        .addGap(143, 143, 143))))
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addComponent(pGameUIBar, javax.swing.GroupLayout.PREFERRED_SIZE, 21, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addGap(98, 98, 98)
                .addComponent(pTowers, javax.swing.GroupLayout.PREFERRED_SIZE, 87, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addContainerGap(394, Short.MAX_VALUE))
        );

        pack();
    }// </editor-fold>//GEN-END:initComponents

    private void formComponentResized(java.awt.event.ComponentEvent evt) {//GEN-FIRST:event_formComponentResized
        if(CurrentGame != null && CurrentGame.isPlaying())
        {
            CanvasDifferenceX = getWidth() / 7*2;
            GameCanvas.setSize(Math.max((this.getWidth() - CanvasDifferenceX) / MapSize.x / 2 * MapSize.x * 2, MapSize.x), 
                               Math.max(((this.getHeight() - CanvasDifferenceY) / MapSize.y / 2) * MapSize.y * 2, MapSize.y));
            SetCompPositions();
            gcWidth = GameCanvas.getWidth();
            gcHeight = GameCanvas.getHeight();
            cellX = gcWidth / MapSize.x;
            cellY = gcHeight / MapSize.y;
            btnSell.setBounds((int)(UIUpgradeX * (double)gcWidth) + GameCanvas.getX(), (int)(UIUpgradeY * (double)gcHeight) + GameCanvas.getY(), 
                            (int)(UIUpgradeSizeX * (double)gcWidth), (int)(UIUpgradeSizeY * (double)gcHeight));
            btnUpgr.setBounds((int)(UISellX * gcWidth) + GameCanvas.getX(), (int)(UISellY * gcHeight) + GameCanvas.getY(), 
                            (int)(UISellSizeX * gcWidth), (int)(UISellSizeY * gcHeight));      
            Graphics g = GameCanvas.getGraphics();
            g.drawImage(CanvasBuffer, 0, 0, GameCanvas.getWidth(), GameCanvas.getHeight(), null);
            g.dispose();
        }
        else
            return;
        //gCanvas.dispose();
    }//GEN-LAST:event_formComponentResized

    private void pGameUIBarComponentResized(java.awt.event.ComponentEvent evt) {//GEN-FIRST:event_pGameUIBarComponentResized
        //64,34,2
        //int i = (jPanel1.getWidth() - 100)/2;
        //jLabel4.setLocation(2, i);
        //lblMobs.setLocation(1, i + 66);
    }//GEN-LAST:event_pGameUIBarComponentResized

    private void formMouseMoved(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_formMouseMoved
        int x = evt.getX(),
            y = evt.getY();
        //Tooltip.show(x, y, null);
        //System.out.println(x + " " + y);
    }//GEN-LAST:event_formMouseMoved

    private void formWindowStateChanged(java.awt.event.WindowEvent evt) {//GEN-FIRST:event_formWindowStateChanged
        int i = getExtendedState();
        if(i != JFrame.ICONIFIED)
            formComponentResized(null);
    }//GEN-LAST:event_formWindowStateChanged

    private void formMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_formMouseClicked

    }//GEN-LAST:event_formMouseClicked

    private void formKeyPressed(java.awt.event.KeyEvent evt) {//GEN-FIRST:event_formKeyPressed
        //Console.AddLine(String.valueOf(evt.getKeyChar()));
    }//GEN-LAST:event_formKeyPressed

    
    
    public static void main(String args[]) {
        /*
         * Set the Nimbus look and feel
         */
        //<editor-fold defaultstate="collapsed" desc=" Look and feel setting code (optional) ">
        /*
         * If Nimbus (introduced in Java SE 6) is not available, stay with the
         * default look and feel. For details see
         * http://download.oracle.com/javase/tutorial/uiswing/lookandfeel/plaf.html
         */
        try {
            for (javax.swing.UIManager.LookAndFeelInfo info : javax.swing.UIManager.getInstalledLookAndFeels()) {
                if ("Nimbus".equals(info.getName())) {
                    javax.swing.UIManager.setLookAndFeel(info.getClassName());
                    break;
                }
            }
        } catch (ClassNotFoundException ex) {
            java.util.logging.Logger.getLogger(MainForm.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        } catch (InstantiationException ex) {
            java.util.logging.Logger.getLogger(MainForm.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        } catch (IllegalAccessException ex) {
            java.util.logging.Logger.getLogger(MainForm.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        } catch (javax.swing.UnsupportedLookAndFeelException ex) {
            java.util.logging.Logger.getLogger(MainForm.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        }
        //</editor-fold>

        /*
         * Create and display the form
         */
        java.awt.EventQueue.invokeLater(new Runnable() {

            public void run() {
                new MainForm().setVisible(true);
            }
        });
    }
    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JLabel jLabel1;
    private javax.swing.JLabel jLabel3;
    private javax.swing.JLabel jLabel4;
    private javax.swing.JLabel jLabel5;
    private javax.swing.JLabel lblGold;
    private javax.swing.JLabel lblLevelInfo;
    private javax.swing.JLabel lblLives;
    private javax.swing.JLabel lblMobKills;
    private javax.swing.JLabel lblMobs;
    private javax.swing.JPanel pGameUIBar;
    private javax.swing.JPanel pGold;
    private javax.swing.JPanel pLives;
    private javax.swing.JPanel pMobKills;
    private javax.swing.JPanel pMobsAlive;
    private javax.swing.JLabel pTowers;
    // End of variables declaration//GEN-END:variables
}
